#include<iostream>
#include<unistd.h>
#include<pthread.h>
#include<queue>

const int cap = 5; //线程池中的线程个数

template<class T>
class ThreadPool
{
public:
  ThreadPool() :_cap(cap)
  {
    pthread_mutex_init(&_mtx,nullptr);
    pthread_cond_init(&_cond,nullptr);
  }
  ~ThreadPool()
  {
    pthread_mutex_destroy(&_mtx);
    pthread_cond_destroy(&_cond);
  }
  bool isEmpty()
  {
    return _task_queue.empty();
  }
  void Lock()
  {
    pthread_mutex_lock(&_mtx);
  }
  void UnLock()
  {
    pthread_mutex_unlock(&_mtx);
  }
  void Wait()
  {
    pthread_cond_wait(&_cond,&_mtx);
  }
  void WaitUp()
  {
    pthread_cond_signal(&_cond);
  }
  void push(const T& task)
  {
    Lock();
    _task_queue.push(task);
    //这里先解锁还是先唤醒都可以
    //如果是先解锁:那么唤醒之后就可以立马竞争锁
    //如果是先唤醒:那么有两种情况:锁已经被释放/锁还没有被释放,但是这两种情况无伤大雅
    //因为pthread_cond_wait函数必须等到竞争锁成功才能返回
    UnLock();
    WaitUp();
  }
  void pop(T* task)
  {
    //由于此时是在加锁的上下文,所以可以直接拿数据
    *task = _task_queue.front();
    _task_queue.pop();
  }
  static void* Rountine(void* arg)
  {
    pthread_detach(pthread_self());//线程分离,不需要主线程进行等待
    ThreadPool<T>* t = (ThreadPool<T>*)arg; 
    while(1)
    {
      t->Lock(); //要访问临界资源-任务队列需要先加锁
      while(t->isEmpty()) //任务队列为空->没有任务
      {
        //本质是这样也可以: pthread_cond_signal(&t->_cond,&t->_mtx); 
        //但是pthread_cond_signal(_cond,_mtx); 是错误的,因为静态成员函数当中没有this指针
        //访问类的非静态成员/成员函数需要通过this指针
         t->Wait();//需要在条件变量下等待
      }
      //来到这里,说明有任务,并且被唤醒了 (竞争锁成功)
      T task;
      t->pop(&task);
      
      //先解锁,再处理任务
      //如果先处理任务再解锁,那么其它线程必须等该线程处理完任务之后才能竞争申请锁在任务队列当中拿任务,仍然是串行化的过程
      //如果是先解锁再处理任务,那么在该线程处理任务的时候,其它线程也可以不断的获取任务,然后执行。 执行任务的过程是并发执行的
      t->UnLock();
      task.Run();
    }
  }
  void InitThreadPool() //线程池的初始化
  {
    pthread_t tid;
    for(int i = 0;i<_cap;i++) //创建cap个线程
    {
      pthread_create(&tid,nullptr,Rountine,(void*)this);
    }
  }
private:
  std::queue<T> _task_queue; //任务队列
  pthread_mutex_t _mtx;//保护任务队列
  pthread_cond_t _cond;//条件变量,当任务队列当中没有任务的时候,需要在该条件下等待,直到外部线程Push了任务
  int _cap;//线程池的线程总数
};
